{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "title: Unsafe pointers\n",
    "description: Using unsafe pointers to access dynamically-allocated memory.\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [`UnsafePointer`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer) type \n",
    "creates an indirect reference to a location in memory.\n",
    "You can use an `UnsafePointer` to dynamically allocate and free memory, or to\n",
    "point to memory allocated by some other piece of code. You can use these\n",
    "pointers to write code that interacts with low-level interfaces, to interface\n",
    "with other programming languages, or to build certain kinds of data structures.\n",
    "But as the name suggests, they're inherently _unsafe_. For example, when using\n",
    "unsafe pointers, you're responsible for ensuring that memory gets allocated and\n",
    "freed correctly.\n",
    "\n",
    ":::note \n",
    "\n",
    "In addition to unsafe pointers, Mojo supports a safe \n",
    "[`Reference`](/mojo/stdlib/memory/reference/Reference) type. See\n",
    "[`UnsafePointer` and `Reference`](#unsafepointer-and-reference) for a brief\n",
    "comparison of the types.\n",
    "\n",
    ":::\n",
    "\n",
    "## What is a pointer?\n",
    "\n",
    "An `UnsafePointer` is a type that holds an address to memory. You can store\n",
    "and retrieve values in that memory. The `UnsafePointer` type is _generic_—it can \n",
    "point to any type of value, and the value type is specified as a parameter. The\n",
    "value pointed to by a pointer is sometimes called a _pointee_."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "from memory.unsafe_pointer import UnsafePointer, initialize_pointee_copy, initialize_pointee_move\n",
    "\n",
    "# Allocate memory to hold a value\n",
    "var ptr = UnsafePointer[Int].alloc(1)\n",
    "# Initialize the allocated memory\n",
    "initialize_pointee_copy(ptr, 100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<figure>\n",
    "\n",
    "  ![](./images/pointer-diagram.png#light)\n",
    "  ![](./images/pointer-diagram-dark.png#dark)\n",
    "\n",
    "<figcaption><b>Figure 1.</b> Pointer and pointee</figcaption>\n",
    "</figure>\n",
    "\n",
    "\n",
    "Accessing the memory—to retrieve or update a value—is called \n",
    "_dereferencing_ the pointer. You can dereference a pointer by following the\n",
    "variable name with an empty pair of square brackets:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "110\n"
     ]
    }
   ],
   "source": [
    "# Update an initialized value\n",
    "ptr[] += 10\n",
    "# Access an initialized value\n",
    "print(ptr[])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also allocate memory to hold multiple values to build array-like\n",
    "structures. For details, see \n",
    "[Storing multiple values](#storing-multiple-values)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lifecycle of a pointer\n",
    "\n",
    "At any given time, a pointer can be in one of several states:\n",
    "\n",
    "- Uninitialized. Just like any variable, a variable of type `UnsafePointer` can\n",
    "  be declared but uninitialized.\n",
    "\n",
    "  \n",
    "  ```mojo\n",
    "  var ptr: UnsafePointer[Int]\n",
    "  ```\n",
    "\n",
    "- Null. A null pointer has an address of 0, indicating an invalid pointer.\n",
    "\n",
    "  ```mojo\n",
    "  ptr = UnsafePointer[Int]()\n",
    "  ```\n",
    "\n",
    "- Pointing to allocated, uninitialized memory. The `alloc()` static method\n",
    "  returns a pointer to a newly-allocated block of memory with space for the \n",
    "  specified number of elements of the pointee's type.\n",
    "\n",
    "  ```mojo\n",
    "  ptr = UnsafePointer[Int].alloc(1)\n",
    "  ```\n",
    "  Trying to dereference a pointer to uninitialized memory results in undefined \n",
    "  behavior. \n",
    "\n",
    "- Pointing to initialized memory. You can initialize an allocated, uninitialized\n",
    "  pointer by moving or copying an existing value into the memory. Or you can use\n",
    "  the `address_of()` static method to get a pointer to an existing value. \n",
    "\n",
    "  ```mojo\n",
    "  initialize_pointee_copy(ptr, value)\n",
    "  # or\n",
    "  initalize_pointee_move(ptr, value^)\n",
    "  # or \n",
    "  ptr = UnsafePointer[Int].address_of(value)\n",
    "  ```\n",
    "  \n",
    "  Once the value is initialized, you can read or mutate it using the dereference\n",
    "  syntax: \n",
    "\n",
    "  ```mojo\n",
    "  oldValue = ptr[]\n",
    "  ptr[] = newValue\n",
    "  ```\n",
    "\n",
    "- Dangling. When you free the pointer's allocated memory, you're left with a \n",
    "  _dangling pointer_. The address still points to its previous location, but the\n",
    "  memory is no longer allocated to this pointer. Trying to dereference the\n",
    "  pointer, or calling any method that would access the memory location results\n",
    "  in undefined behavior.\n",
    "\n",
    "  ```mojo\n",
    "  ptr.free()\n",
    "  ```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "The following diagram shows the lifecycle of an `UnsafePointer`:\n",
    "\n",
    "<figure>\n",
    "\n",
    "  ![](./images/pointer-lifecycle.png#light)\n",
    "  ![](./images/pointer-lifecycle-dark.png#dark)\n",
    "\n",
    "<figcaption><b>Figure 2.</b> Lifecycle of an <code>UnsafePointer</code></figcaption>\n",
    "</figure>\n",
    "\n",
    "### Allocating memory\n",
    "\n",
    "Use the static `alloc()` method to allocate memory. The method returns a new\n",
    "pointer pointing to the requested memory. You can allocate space for one or \n",
    "more values of the pointee's type.\n",
    "\n",
    "```mojo\n",
    "ptr = UnsafePointer[Int].alloc(10) # Allocate space for 10 Int values\n",
    "```\n",
    "\n",
    "The allocated space is _uninitialized_—like a variable that's been declared but\n",
    "not initialized.\n",
    "\n",
    "### Initializing the pointee\n",
    "\n",
    "The `unsafe_pointer` module includes a number of free functions for working with\n",
    "the `UnsafePointer` type. To initialize allocated memory, you can use the \n",
    "[`initialize_pointee_copy()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_copy)\n",
    "or [`initialize_pointee_move()`](/mojo/stdlib/memory/unsafe_pointer/initialize_pointee_move)\n",
    "functions:\n",
    "\n",
    "```mojo\n",
    "initialize_pointee_copy(ptr, 5)\n",
    "```\n",
    "\n",
    "To move a value into the pointer's memory location, use\n",
    "`initialize_pointee_move()`:\n",
    "\n",
    "```mojo\n",
    "initialize_pointee_move(str_ptr, my_string^)\n",
    "```\n",
    "\n",
    "Note that to move the value, you usually need to add the transfer operator\n",
    "(`^`), unless the value is a [trivial\n",
    "type](/mojo/manual/types#register-passable-memory-only-and-trivial-types) (like\n",
    "`Int`) or a newly-constructed, \"owned\" value:\n",
    "\n",
    "```mojo\n",
    "initialize_pointee_move(str_ptr, str(\"Owned string\"))\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Alternately, you can get a pointer to an existing value using the static \n",
    "`address_of()` method. This is useful for getting a pointer to a value on the \n",
    "stack, for example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "var counter: Int = 5\n",
    "ptr = UnsafePointer[Int].address_of(counter)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that when calling `address_of()`, you don't need to allocate memory ahead\n",
    "of time, since you're pointing to an existing value."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "#### Initializing from an address\n",
    "\n",
    "When exchanging data with other programming languages, you may need to construct\n",
    "an `UnsafePointer` from an address. For example, if you're working with a\n",
    "pointer allocated by a C or C++ library, or a Python object that implements the\n",
    "[array interface protocol](https://numpy.org/doc/stable/reference/arrays.interface.html),\n",
    "you can construct an `UnsafePointer` to access the data from the Mojo side.\n",
    "\n",
    "You can construct an `UnsafePointer` from an integer address using the `address`\n",
    "keyword argument. For example, the following code creates a NumPy array and then\n",
    "accesses the data using a Mojo pointer:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1, 2, 3, 4, 5, 6, 7, 8, 9, "
     ]
    }
   ],
   "source": [
    "from python import Python\n",
    "from memory.unsafe_pointer import UnsafePointer\n",
    "\n",
    "def share_array():\n",
    "    np = Python.import_module(\"numpy\")\n",
    "    arr = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "    addr = int(arr.__array_interface__[\"data\"][0])\n",
    "    ptr = UnsafePointer[Int64](address=addr)\n",
    "    for i in range(9):\n",
    "        print(ptr[i], end=\", \")\n",
    "\n",
    "share_array()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When dealing with memory allocated elsewhere, you need to be aware of who's\n",
    "responsible for freeing the memory. Freeing memory allocated elsewhere \n",
    "can result in undefined behavior.\n",
    "\n",
    "You also need to be aware of the format of the data stored in memory, including\n",
    "data types and byte order. For more information, see \n",
    "[Converting data: bitcasting and byte order](#converting-data-bitcasting-and-byte-order)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "### Dereferencing pointers\n",
    "\n",
    "Use the `[]` dereference operator to access the value stored at a pointer (the\n",
    " \"pointee\").\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "110\n"
     ]
    }
   ],
   "source": [
    "# Read from pointee\n",
    "print(ptr[])\n",
    "# mutate pointee\n",
    "ptr[] = 0\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "If you've allocated space for multiple values, you can use subscript syntax to\n",
    "access the values, as if they were an array, like `ptr[3]`. The empty subscript\n",
    "`[]` has the same meaning as `[0]`.\n",
    "\n",
    ":::caution\n",
    "\n",
    "The dereference operator assumes that the memory being dereferenced is \n",
    "initialized. Dereferencing uninitialized memory results in undefined behavior.\n",
    "\n",
    ":::\n",
    "\n",
    "You cannot safely use the dereference operator on uninitialized memory, even to\n",
    "_initialize_ a pointee. This is because assigning to a dereferenced pointer\n",
    "calls lifecycle methods on the existing pointee (such as the destructor, move\n",
    "constructor or copy constructor).\n",
    "\n",
    "```mojo\n",
    "str_ptr = UnsafePointer[String].alloc(1)\n",
    "# str_ptr[] = \"Testing\" # Undefined behavior!\n",
    "initialize_pointee_move(str_ptr, \"Testing\")\n",
    "str_ptr[] += \" pointers\" # Works now\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Destroying or removing values\n",
    "\n",
    "The \n",
    "[`move_from_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/move_from_pointee)\n",
    "function moves the pointee from the memory location pointed to by `ptr`. This is\n",
    "a consuming move—it invokes `__moveinit__()` on the destination value. It leaves\n",
    "the memory location uninitialized.\n",
    "\n",
    "The [`destroy_pointee(ptr)`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee)\n",
    "function calls the destructor on the pointee, and leaves the memory location\n",
    "pointed to by `ptr` uninitialized. \n",
    "\n",
    "Both `move_from_pointee()` and `destroy_pointee()` require that the pointer is \n",
    "non-null, and the memory location contains a valid, initialized value of the \n",
    "pointee's type; otherwise the function results in undefined behavior.\n",
    "\n",
    "The [`move_pointee(src, dst)`](/mojo/stdlib/memory/unsafe_pointer/move_pointee)\n",
    "function moves the pointee from one pointer location to another. Both pointers\n",
    "must be non-null. The source location must contain a valid, initialized value of \n",
    "the pointee's type, and is left uninitialized after the call. The destination \n",
    "location is assumed to be uninitialized—if it contains a valid value, that\n",
    "value's destructor is not run. The value from the source location is moved to\n",
    "the destination location as a consuming move. This function also has undefined\n",
    "behavior if any of its prerequisites is not met."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Freeing memory\n",
    "\n",
    "Calling [`free()`](/mojo/stdlib/memory/unsafe_pointer/UnsafePointer#free) on a \n",
    "pointer frees the memory allocated by the pointer. It doesn't call the \n",
    "destructors on any values stored in the memory—you need to do that explicitly\n",
    "(for example, using\n",
    "[`destroy_pointee()`](/mojo/stdlib/memory/unsafe_pointer/destroy_pointee) or\n",
    "one of the other functions described in \n",
    "[Destroying or removing values](#destroying-or-removing-values)).\n",
    "\n",
    "Disposing of a pointer without freeing the associated memory can result in a\n",
    "memory leak—where your program keeps taking more and more memory, because not\n",
    "all allocated memory is being freed.\n",
    "\n",
    "On the other hand, if  you have multiple copies of a pointer accessing the same\n",
    "memory, you need to make sure you only call `free()` on one of them. Freeing the\n",
    "same memory twice is also an error.\n",
    "\n",
    "After freeing a pointer's memory, you're left with a dangling pointer—its\n",
    "address still points to the freed memory. Any attempt to access the memory,\n",
    "like dereferencing the pointer results in undefined behavior.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Storing multiple values\n",
    "\n",
    "As mentioned in [Allocating memory](#allocating-memory), you can use an \n",
    "`UnsafePointer` to allocate memory for multiple values. The memory is allocated\n",
    "as a single, contiguous block. Pointers support arithmetic: adding an integer\n",
    "to a pointer returns a new pointer offset by the specified number of values from\n",
    "the original pointer:\n",
    "\n",
    "```mojo\n",
    "third_ptr = first_ptr + 2\n",
    "```\n",
    "\n",
    "Pointers also support subtraction, as well as in-place addition and subtraction:\n",
    "\n",
    "```mojo\n",
    "# Advance the pointer one element:\n",
    "ptr += 1\n",
    "```\n",
    "\n",
    "<figure>\n",
    "\n",
    "  ![](./images/pointer-offset.png#light)\n",
    "  ![](./images/pointer-offset-dark.png#dark)\n",
    "\n",
    "<figcaption><b>Figure 3.</b> Pointer arithmetic</figcaption>\n",
    "</figure>\n",
    "\n",
    "For example, the following example allocates memory to store 6 `Float64`\n",
    "values, and initializes them all to zero."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "float_ptr = UnsafePointer[Float64].alloc(6)\n",
    "for offset in range(6):\n",
    "    initialize_pointee_copy(float_ptr+offset, 0.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once the values are initialized, you can access them using subscript syntax:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.0, 0.0, 3.0, 0.0, 0.0, 0.0, "
     ]
    }
   ],
   "source": [
    "float_ptr[2] = 3.0\n",
    "for offset in range(6):\n",
    "    print(float_ptr[offset], end=\", \")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Converting data: bitcasting and byte order\n",
    "\n",
    "Bitcasting a pointer returns a new pointer that has the same memory location,\n",
    "but a new data type. This can be useful if you need to access different types of\n",
    "data from a single area of memory. This can happen when you're reading binary\n",
    "files, like image files, or receiving data over the network.\n",
    "\n",
    "The following sample processes a format that consists of chunks of data,\n",
    "where each chunk contains a variable number of 32-bit integers.\n",
    "Each chunk begins with an 8-bit integer that identifies the number of values\n",
    "in the chunk."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "def read_chunks(owned ptr: UnsafePointer[UInt8]) -> List[List[UInt32]]:\n",
    "    chunks = List[List[UInt32]]()\n",
    "    # A chunk size of 0 indicates the end of the data\n",
    "    chunk_size = int(ptr[])\n",
    "    while (chunk_size > 0):\n",
    "        # Skip the 1 byte chunk_size and get a pointer to the first\n",
    "        # UInt32 in the chunk\n",
    "        ui32_ptr = (ptr + 1).bitcast[UInt32]()\n",
    "        chunk = List[UInt32](capacity=chunk_size)\n",
    "        for i in range(chunk_size):\n",
    "            chunk.append(ui32_ptr[i])\n",
    "        chunks.append(chunk)\n",
    "        # Move our pointer to the next byte after the current chunk\n",
    "        ptr += (1 + 4 * chunk_size)\n",
    "        # Read the size of the next chunk\n",
    "        chunk_size = int(ptr[])\n",
    "    return chunks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When dealing with data read in from a file or from the network, you may also\n",
    "need to deal with byte order. Most systems use little-endian byte order (also\n",
    "called least-signficicant byte, or LSB) where the least-significant byte in a\n",
    "multibyte value comes first. For example, the number 1001 can be represented in\n",
    "hexadecimal as 0x03E9, where E9 is the least-significant byte. Represented as a\n",
    "16-bit little-endian integer, the two bytes are ordered E9 03. As a 32-bit \n",
    "integer, it would be represented as E9 03 00 00. \n",
    "\n",
    "Big-endian or most-significant byte (MSB) ordering is the opposite: in the \n",
    "32-bit case, 00 00 03 E9. MSB ordering is frequently used in file formats and\n",
    "when transmitting data over the network. You can use the \n",
    "[`byte_swap()`](/mojo/stdlib/bit/bit/byte_swap) function to swap the byte\n",
    "order of  a SIMD value from big-endian to little-endian or the reverse. For\n",
    "example, if the method above was reading big-endian data, you'd just need to\n",
    "change a single line:\n",
    "\n",
    "```mojo\n",
    "chunk.append(byte_swap(ui32_ptr[i]))\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `DTypePointer`: handling numeric data\n",
    "\n",
    "A [`DTypePointer`](/mojo/stdlib/memory/unsafe/DTypePointer) is an unsafe\n",
    "pointer that supports some additional methods for loading and storing numeric\n",
    "data. Like the [`SIMD`](/mojo/stdlib/builtin/simd/SIMD) type, it's parameterized\n",
    "on [`DType`](/mojo/stdlib/builtin/dtype/DType) as described in \n",
    "[SIMD and DType](/mojo/manual/types#simd-and-dtype).\n",
    "\n",
    "`DTypePointer` has a similar API to `UnsafePointer`:\n",
    "\n",
    "- You can [`alloc()`](/mojo/stdlib/memory/unsafe/DTypePointer#alloc) and\n",
    "  [`free()`](/mojo/stdlib/memory/unsafe/DTypePointer#free) memory, or use \n",
    "  [`address_of()`](/mojo/stdlib/memory/unsafe/DTypePointer#address_of) to point\n",
    "  to an existing value.\n",
    "- The pointer supports pointer arithmetic to access adjacent memory locations.\n",
    "- You can dereference a `DTypePointer` using subscript notation.\n",
    "- You can construct a `DTypePointer` from an `Int` address.\n",
    "\n",
    "\n",
    "You can also construct a `DTypePointer` from an `UnsafePointer` of a scalar\n",
    "type like `Int64` or `Float32`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "from memory import DTypePointer, UnsafePointer\n",
    "\n",
    "uptr = UnsafePointer[Float64].alloc(10)\n",
    "dptr = DTypePointer(uptr)\n",
    "# Or:\n",
    "dptr = DTypePointer[DType.float64].alloc(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Unlike `UnsafePointer`, `DTypePointer` doesn't have special methods to\n",
    "initialize values, destroy them, or move them out. Because all of the values\n",
    "that `DTypePointer` works with are trivial types, `DTypePointer` doesn't need to\n",
    "destroy values before overwriting them or freeing memory. Instead, you can use\n",
    "subscript notation (like `UnsafePointer`) or use the\n",
    "[`load()`](/mojo/stdlib/memory/unsafe/DTypePointer#load) and \n",
    "[`store()`](/mojo/stdlib/memory/unsafe/DTypePointer#store) methods to access \n",
    "values.\n",
    "\n",
    "What `DTypePointer` adds is various methods of loading and storing SIMD values\n",
    "to  memory. In particular: strided load/store and gather/scatter.\n",
    "\n",
    "Strided load loads values from memory into a SIMD vector using an offset (the\n",
    "\"stride\") between successive memory addresses. This can be useful for\n",
    "extracting rows or columns from tabular data, or for extracting individual\n",
    "values from structured data. For example, consider the data for an RGB image,\n",
    "where each pixel is made up of three 8-bit values, for red, green, and blue. If\n",
    "you want to access just the red values, you can use a strided load or store.\n",
    "\n",
    "<figure>\n",
    "\n",
    "  ![](./images/strided-load-storage.png#light)\n",
    "  ![](./images/strided-load-storage-dark.png#dark)\n",
    "\n",
    "<figcaption><b>Figure 4.</b> Strided load</figcaption>\n",
    "</figure>\n",
    "\n",
    "The following function uses the \n",
    "[`simd_strided_load()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_load)\n",
    "and \n",
    "[`simd_strided_store()`](/mojo/stdlib/memory/unsafe/DTypePointer#simd_strided_store)\n",
    "methods to invert the red pixel values in an image, 8 values at a time. (Note\n",
    "that this function only handles images where the number of pixels is evenly\n",
    "divisible by eight.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def invert_red_channel(ptr: DTypePointer[DType.uint8], pixel_count: Int):\n",
    "    # number of values loaded or stored at a time\n",
    "    alias simd_width = 8\n",
    "    # bytes per pixel, which is also the stride size\n",
    "    bpp = 3\n",
    "    for i in range(0, pixel_count * bpp, simd_width * bpp):\n",
    "        red_values = ptr.offset(i).simd_strided_load[width=simd_width](bpp)\n",
    "        # Invert values and store them in their original locations\n",
    "        ptr.offset(i).simd_strided_store[width=simd_width](~red_values, bpp)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":::note Future of `DTypePointer`\n",
    "\n",
    "The `DTypePointer` type exists for historical reasons, but it no longer really\n",
    "needs to be a separate type. `UnsafePointer` can handle most things that\n",
    "`DTypePointer` does except for a few features related to reading and writing\n",
    "`SIMD` values. At some point in the future, these features will probably be\n",
    "integrated into the `SIMD` type, so you can use them with `UnsafePointer`.\n",
    "\n",
    ":::"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Safety\n",
    "\n",
    "Unsafe pointers are unsafe for several reasons:\n",
    "\n",
    "- Memory management is up to the user. You need to manually allocate\n",
    "  and free memory, and be aware of when other APIs are allocating or freeing\n",
    "  memory for you.\n",
    "\n",
    "- `UnsafePointer` and `DTypePointer` values are _nullable_—that is, the pointer\n",
    "  is not guaranteed to point to anything. And even when a pointer points to\n",
    "  allocated memory, that memory may not be _initialized_.\n",
    "\n",
    "- Mojo doesn't track lifetimes for the data pointed to by an `UnsafePointer`.\n",
    "  When you use an `UnsafePointer`, managing memory and knowing when to destroy\n",
    "  objects is your responsibility. (Since `DTypePointer` only works with trivial\n",
    "  types, this is not typically an issue for `DTypePointer`.)\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `UnsafePointer` and `Reference`\n",
    "\n",
    "The [`Reference`](/mojo/stdlib/memory/reference/Reference) type is essentially a \n",
    "safe pointer type. Like a pointer, you can derferences a `Reference` using the \n",
    "dereference operator, `[]`. However, the `Reference` type has several\n",
    "differences from `UnsafePointer` which make it safer:\n",
    "\n",
    "- A `Reference` is _non-nullable_. A reference always points to something.\n",
    "- You can't allocate or free memory using a `Reference`—only point to an\n",
    "  existing value.\n",
    "- A `Reference` only refers to a single value. You can't do pointer arithmetic\n",
    "  with a `Reference`.\n",
    "- A `Reference` has an associated _lifetime_, which connects it back to an\n",
    "  original, owned value. The lifetime ensures that the value won't be destroyed\n",
    "  while the reference exists.\n",
    "\n",
    "The `Reference` type shouldn't be confused with the immutable and mutable\n",
    "references used with the `borrowed` and `inout` argument conventions. Those\n",
    "references do not require explicit dereferencing, unlike a `Reference` or \n",
    "`UnsafePointer`."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Mojo",
   "language": "mojo",
   "name": "mojo-jupyter-kernel"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "mojo"
   },
   "file_extension": ".mojo",
   "mimetype": "text/x-mojo",
   "name": "mojo"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
